package main

import (
	"bufio"
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"strings"
)

type CompanyInfo struct {
	Company    string `json:"company"`
	Department string `json:"department"`
	Uid        string `json:"uid"`
	Expired    int    `json:"expired"`
	Host       string `json:"host"`
	Port       int    `json:"port"`
	Database   string `json:"database"`
	User       string `json:"user"`
	Password   string `json:"password"`
}

func main() {
	//key的长度必须都是8位
	var key = "66227638"
	//定义变量接收命令行参数
	var company string
	var department string
	var uid string
	var expired int
	var filename string
	var host string
	var port int
	var database string
	var user string
	var password string
	//获取命令行参数
	flag.StringVar(&company, "c", "", "公司名称")
	flag.StringVar(&department, "d", "", "门店或部门名称")
	flag.StringVar(&uid, "u", "", "门店编号")
	flag.IntVar(&expired, "e", 0, "到期时间，如20211231")
	flag.StringVar(&filename, "f", "", "指定文件查看内容")
	flag.StringVar(&host, "o", "", "数据库服务器地址")
	flag.StringVar(&database, "a", "", "数据库名称")
	flag.IntVar(&port, "r", 3306, "数据库端口")
	flag.StringVar(&user, "s", "", "数据库登录用户")
	flag.StringVar(&password, "p", "", "数据库登录密码")
	flag.Parse()

	if strings.Trim(filename, "") == "" {
		if (strings.Trim(company, "") == "") ||
			(strings.Trim(department, "") == "") ||
			(strings.Trim(uid, "") == "") ||
			(strings.Trim(host, "") == "") ||
			(strings.Trim(database, "") == "") ||
			(strings.Trim(user, "") == "") {
			fmt.Println("参数不完整，请使用 -h 参数查看用法")
			os.Exit(0)
		}
		//加密文件
		var body CompanyInfo
		body.Company = company
		body.Department = department
		body.Uid = uid
		body.Expired = expired
		body.Host = host
		body.Database = database
		body.Port = port
		body.User = user
		body.Password = password

		//转换成JSON
		b, err := json.Marshal(body)
		if err != nil {
			fmt.Println("JSON ERR:", err)
		} else {
			fmt.Println(string(b))
		}
		//加密
		EncStr := EncryptdesCbc(string(b), key)
		//保存到文本文件
		f, err := os.OpenFile(body.Uid+".pem", os.O_CREATE|os.O_WRONLY, 0644)
		defer f.Close()
		if err != nil {
			fmt.Println(err.Error())
		} else {
			f.WriteString(EncStr)
		}
		//DecStr := DecryptdesCbc(EncStr, key)
		//fmt.Println(DecStr)
		fmt.Println("\r\n" + uid + ".pem已生成")
	} else {
		//解密文件，读文本
		f, err := os.OpenFile(filename, os.O_RDONLY, 0600)
		defer f.Close()
		if err != nil {
			fmt.Println(err.Error())
		} else {
			reader := bufio.NewReader(f)
			str, _ := reader.ReadString('\n')
			DecStr := DecryptdesCbc(str, key)
			fmt.Println(DecStr)
		}
	}
}

/*EncStr = EncryptdesEcb(info, key)
fmt.Println(EncStr)
DecStr = DecryptdesEcb(EncStr, key)
fmt.Println(DecStr)*/

//CBC加密
func EncryptdesCbc(src, key string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	data = PKCS5Padding(data, block.BlockSize())
	//获取CBC加密模式
	iv := keyByte //用密钥作为向量(不建议这样使用)
	mode := cipher.NewCBCEncrypter(block, iv)
	out := make([]byte, len(data))
	mode.CryptBlocks(out, data)
	return fmt.Sprintf("%X", out)
}

//CBC解密
func DecryptdesCbc(src, key string) string {
	keyByte := []byte(key)
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	iv := keyByte //用密钥作为向量(不建议这样使用)
	mode := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(data))
	mode.CryptBlocks(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext)
}

//ECB加密
func EncryptdesEcb(src, key string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	//对明文数据进行补码
	data = PKCS5Padding(data, bs)
	if len(data)%bs != 0 {
		panic("Need a multiple of the blocksize")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		//对明文按照blocksize进行分块加密
		//必要时可以使用go关键字进行并行加密
		block.Encrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	return fmt.Sprintf("%X", out)
}

//ECB解密
func DecryptdesEcb(src, key string) string {
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	if len(data)%bs != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		block.Decrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	out = PKCS5UnPadding(out)
	return string(out)
}

//明文补码算法
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

//明文减码算法
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
